package com.opdar.platform.core.base;

import com.opdar.platform.annotations.JSON;
import com.opdar.platform.core.convert.*;
import org.apache.commons.fileupload.FileItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.sql.Timestamp;
import java.util.*;

/**
 * Created by shiju on 2017/1/18.
 */
public class MethodInvokeHandler {
    private Method method;
    private String[] parameterNames;
    private List<ParameterConvert<?>> convertList;
    private List<Interceptor> interceptors = new LinkedList<Interceptor>();
    private List<ErrorHandler> errorHandlers = new LinkedList<ErrorHandler>();
    private List<Editor> editorList = new LinkedList<Editor>();
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private JSONConvert jsonConvert = new DefaultJSONConvert();

    public MethodInvokeHandler setJsonConvert(JSONConvert jsonConvert) {
        if(jsonConvert != null){
            this.jsonConvert = jsonConvert;
        }
        return this;
    }

    public void addInterceptors(Interceptor interceptor) {
        this.interceptors.add(interceptor);
    }

    public void setInterceptors(List<Interceptor> interceptors) {
        this.interceptors.addAll(interceptors);
    }

    public List<Interceptor> getInterceptors() {
        return interceptors;
    }

    public List<Editor> getEditorList() {
        return editorList;
    }

    public void setEditorList(List<Editor> editorList) {
        this.editorList = editorList;
    }

    public MethodInvokeHandler(Method method, String[] parameterNames) {
        this.method = method;
        this.parameterNames = parameterNames;
    }

    public Class<?> getParentClass() {
        return method.getDeclaringClass();
    }

    public String getMethodName() {
        return method.getName();
    }

    public Class<?> getReturnType() {
        return method.getReturnType();
    }

    public Object invoke(Object o, Map<String, ?> parameters) {
        Object[] _parameters = new Object[parameterNames.length];
        if (_parameters.length > 0) {
            for (int i = 0; i < parameterNames.length; i++) {
                String name = parameterNames[i];
                if (parameters.containsKey(name)) {
                    _parameters[i] = parameters.get(name);
                }else
                if (parameters.containsKey(name.concat("[]"))) {
                    _parameters[i] = parameters.get(name.concat("[]"));
                }
            }
            Annotation[][] annotations = method.getParameterAnnotations();
            Type[] parameterTypes = method.getGenericParameterTypes();
            if (convertList == null) {
                convertList = new LinkedList<ParameterConvert<?>>();
                for (int i = 0; i < parameterTypes.length; i++) {
                    Type type = parameterTypes[i];
                    JSON json = findAnnotation(annotations[i],JSON.class);
                    ParameterConvert<?> convert = null;
                    if(json != null){
                        convert = jsonConvert;
                    }else{
                        convert = ParameterConvertManager.getParameterConvert(type);
                    }
                    convertList.add(convert);
                }
            }
            for (int i = 0; i < convertList.size(); i++) {
                ParameterConvert<?> convert = convertList.get(i);
                if (convert != null) {
                    try {
                        Object parameter = null;
                        if(convert instanceof JSONConvert){
                            byte[] body = Context.getBody("application/json");
                            if(body == null || body.length == 0)continue;
                            Type type = parameterTypes[i];
                            parameter = ((JSONConvert) convert).convert(body,type);
                        } else if (convert instanceof ObjectConvert) {
                            parameter = convert.convert(parameters);
                        } else {
                            parameter = convert.convert(_parameters[i]);
                        }
                        _parameters[i] = parameter;
                    } catch (Exception e) {
                        logger.info("Parse {} error .{}", parameterNames[i], e);
                    }
                }
            }
        }
        Object invokeObj = ReflectionUtils.invokeMethod(method, o, _parameters);

        if(editorList.size() > 0){
            for(Editor editor:editorList){
                invokeObj = editor.editor(invokeObj);
            }
        }
        return invokeObj;
    }

    private <T extends Annotation>T findAnnotation(Annotation[] annotations, Class<T> annotationClass) {
        T annotation = null;
        for(Annotation _annotation: annotations){
            if(annotationClass.isAssignableFrom(_annotation.annotationType())){
                annotation = (T) _annotation;
            }
        }
        return annotation;
    }

    public void setErrorHandlers(List<ErrorHandler> errorHandlers) {
        this.errorHandlers = errorHandlers;
    }

    public List<ErrorHandler> getErrorHandlers() {
        return errorHandlers;
    }
}
